Personal tools

Lua/Tutorials/Player values

From JC2-MP Documentation

< Lua‎ | Tutorials
Jump to: navigation, search

Values can be stored on players. These values can be accessed cross-module, and it's easy to sync them across server and client. They are persistent until the player disconnects. There are also events for when player values change.

Example scenarios where storing values on players is useful

  • Players can choose their nickname when they join, which is done in one module; what if some other module wants to get their nickname, such as a nametags script?
  • Each player can be part of a faction; if a client aims at a player, it should show whether they're on the same faction or not.
  • Players can have hats; client scripts must place the appropriate ClientStaticObjects on players' heads, but client scripts must know what hat model each player has, and must know when their hat changes.

These can all be solved using various other methods, but the player value system makes it so much easier.

Relevant functions and events

How it works

An internal key-value map is kept on each player. Server and client maintain their own independent set of players and thus maps.

  • Server-side: When a player joins, their map is empty.
  • Client-side: When you join a server, each player's map is empty.

Player:SetValue can alter a player's map, and Player:GetValue can be used to get a value from their map. Note how these relate to server and client: Player:SetValue on the server will not affect the result of Player:GetValue on the client, and vice versa.

Player:SetNetworkValue is a server-only function that alters players both on the server and on every client. It is equivalent to running Player:SetValue on the server and having every client run Player:SetValue.

The SharedObjectValueChange event is fired when Player:SetValue is called.

The NetworkObjectValueChange event is fired on both client and server when Player:SetNetworkValue is called.

Examples

The simplist example (client)

print("MyKey:", LocalPlayer:GetValue("MyKey"))
 
LocalPlayer:SetValue("MyKey", "I'm a value")
 
print("MyKey:", LocalPlayer:GetValue("MyKey"))

This should output "nil" and "I'm a value" the first time it is run. If you reload the module, it will output "I'm a value" twice (player storage is persistent). If you reconnect, the value will be deleted and you will get the first result again.

Console command to get/set values

Use 'set playername key value' and 'get playername key' with the console to test it. You can use 'set playername key' (no value argument) to remove values.

This can be used as either a client or server script. If you use it as both, you will notice that setting a player value on the server has no affect on any client and vice versa.

  1. function SharedObjectValueChange(args)
  2. 	if args.object.__type ~= "Player" then return end
  3. 	print(tostring(args.object).."'s "..args.key.." was set to "..tostring(args.value))
  4. end
  5. Events:Subscribe("SharedObjectValueChange", SharedObjectValueChange)
  6.  
  7. function ConsoleSet(args)
  8. 	local words = args.text:split(" ")
  9.  
  10. 	if #words == 2 or #words == 3 then
  11. 		local player = Player.Match(words[1])[1]
  12. 		if player then
  13. 			player:SetValue(words[2], words[3])
  14. 		else
  15. 			print("Player not found!")
  16. 		end
  17. 	else
  18. 		print("Invalid arguments!")
  19. 	end
  20. end
  21. Console:Subscribe("set", ConsoleSet)
  22.  
  23. function ConsoleGet(args)
  24. 	local words = args.text:split(" ")
  25.  
  26. 	if #words == 2 then
  27. 		local player = Player.Match(words[1])[1]
  28. 		if player then
  29. 			local value = player:GetValue(words[2])
  30. 			print(player:GetName().."'s "..words[2].." is currently "..tostring(value))
  31. 		else
  32. 			print("Player not found!")
  33. 		end
  34. 	else
  35. 		print("Invalid arguments!")
  36. 	end
  37. end
  38. Console:Subscribe("get", ConsoleGet)

SetNetworkValue

This is similar to above, but the server-side uses SetNetworkValue. If you set a value on the server, it propagates for all clients.

Note that you can set a value on the client-side, which will result in the server and client disagreeing with each other.

Server

  1. function ConsoleSetNetwork(args)
  2. 	local words = args.text:split(" ")
  3.  
  4. 	if #words == 2 or #words == 3 then
  5. 		local player = Player.Match(words[1])[1]
  6. 		if player then
  7. 			player:SetNetworkValue(words[2], words[3])
  8. 		else
  9. 			print("Player not found!")
  10. 		end
  11. 	else
  12. 		print("Invalid arguments!")
  13. 	end
  14. end
  15. Console:Subscribe("set", ConsoleSetNetwork)
  16.  
  17. function ConsoleGet(args)
  18. 	local words = args.text:split(" ")
  19.  
  20. 	if #words == 2 then
  21. 		local player = Player.Match(words[1])[1]
  22. 		if player then
  23. 			local value = player:GetValue(words[2])
  24. 			print(player:GetName().."'s "..words[2].." is currently "..tostring(value))
  25. 		else
  26. 			print("Player not found!")
  27. 		end
  28. 	else
  29. 		print("Invalid arguments!")
  30. 	end
  31. end
  32. Console:Subscribe("get", ConsoleGet)

Client

  1. function ConsoleSet(args)
  2. 	local words = args.text:split(" ")
  3.  
  4. 	if #words == 2 or #words == 3 then
  5. 		local player = Player.Match(words[1])[1]
  6. 		if player then
  7. 			player:SetValue(words[2], words[3])
  8. 		else
  9. 			print("Player not found!")
  10. 		end
  11. 	else
  12. 		print("Invalid arguments!")
  13. 	end
  14. end
  15. Console:Subscribe("set", ConsoleSet)
  16.  
  17. function ConsoleGet(args)
  18. 	local words = args.text:split(" ")
  19.  
  20. 	if #words == 2 then
  21. 		local player = Player.Match(words[1])[1]
  22. 		if player then
  23. 			local value = player:GetValue(words[2])
  24. 			print(player:GetName().."'s "..words[2].." is currently "..tostring(value))
  25. 		else
  26. 			print("Player not found!")
  27. 		end
  28. 	else
  29. 		print("Invalid arguments!")
  30. 	end
  31. end
  32. Console:Subscribe("get", ConsoleGet)

Notes

  • You can set values to nil: player:SetValue("MyKey", nil) (or player:SetValue("MyKey"))
  • You can subscribe the same function to both SharedObjectValueChange and NetworkObjectValueChange, which is useful for knowing when a value changes without caring what changed it.
  • Keep in mind that the server and each individual client can all have differing values for each player.
  • It is not recommended to set values on player variables directly. Something like player.myVariable = "blar" can result in strange, undefined behaviour, and will only apply to that Lua module. Similar results apply to other classes, such as Vehicle or World. This is one of the motivations for the player value system.